﻿using System.ComponentModel.Composition;
using System.Diagnostics;
using MongoDB.Bson;
using MongoDB.Driver.Linq;
using OperationPlugins.Applications.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Remoting.Messaging;
using System.Configuration;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace OperationPlugins.Applications.MongoDB
{
    [Export(typeof(IApplicationDataContext))]
    [Serializable]
    public sealed class MongoDBApplicationDataContext : MarshalByRefObject, ILogicalThreadAffinative, IApplicationDataContext
    {
        /// <summary>
        /// Gets the master database.
        /// </summary>
        /// <returns>The master database.</returns>
        private MongoDatabase GetMasterDatabase()
        {
            ConnectionStringBuilder csb = new ConnectionStringBuilder();
            string cs = csb.GetConnectionString();

            DatabaseNameBuilder dbnb = new DatabaseNameBuilder();
            string dbn = dbnb.GetDatabaseName();

            MongoClient client = new MongoClient(cs);
            MongoServer server = client.GetServer();
            MongoDatabase database = server.GetDatabase(dbn);
            return database;
        }

        /// <summary>
        /// Gets the database for the installed application.
        /// </summary>
        /// <param name="index">The installed application</param>
        /// <returns>The database for the specified installed application.</returns>
        private MongoDatabase GetAppInstanceDatabase(AppInstanceIndex index)
        {
            if (index == null)
            {
                throw new ArgumentNullException("index");
            }

            ConnectionStringBuilder csb = new ConnectionStringBuilder(index);
            string cs = csb.GetConnectionString();

            DatabaseNameBuilder dbnb = new DatabaseNameBuilder(index);
            string dbn = dbnb.GetDatabaseName();

            MongoClient client = new MongoClient(cs);
            MongoServer server = client.GetServer();
            MongoDatabase database = server.GetDatabase(dbn);
            return database;
        }

        /// <summary>
        /// AppInstanceIndex objects in the master database.
        /// </summary>
        private MongoCollection<AppInstanceIndex> Indexes
        {
            get { return GetMasterDatabase().GetCollection<AppInstanceIndex>("index"); }
        }


        /// <summary>
        /// Creates a new application installation.
        /// </summary>
        /// <param name="installation">The application installation data.</param>
        /// <returns>The installed application.</returns>
        AppInstance IApplicationDataContext.NewAppInstallation(AppInstallation installation)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets all application installations.
        /// </summary>
        /// <returns>All application installations.</returns>
        IEnumerable<AppInstallation> IApplicationDataContext.GetAppInstallations()
        {
            AppInstanceIndex[] indexes = Indexes.FindAll().ToArray();

            return indexes.Select(index => new AppInstallation
            {
                AppId = index.AppId,
                Name = index.Name,
                Version = index.Version,
                UseDefaultConnectionString = index.UseDefaultConnectionString,
                ConnectionString = index.ConnectionString,
                DatabaseName = index.DatabaseName,
                IsEnabled = index.IsEnabled
            });
        }

        /// <summary>
        /// Gets the data of application instance by id.
        /// </summary>
        /// <param name="id">id</param>
        /// <returns>The requested application instance of null if not found.</returns>
        AppInstance IApplicationDataContext.GetAppInstanceById(string id)
        {
            if (id == null)
            {
                throw new ArgumentNullException("id");
            }

            


            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the data of application instace by application id.
        /// </summary>
        /// <param name="appId">application id</param>
        /// <returns>The requested application instance of null if not found.</returns>
        AppInstance IApplicationDataContext.GetAppInstaceByAppId(string appId)
        {
            if (appId == null)
            {
                throw new ArgumentNullException("appId");
            }

            MongoCollection<AppInstanceIndex> indexes = GetMasterDatabase().GetCollection<AppInstanceIndex>("index");
            AppInstanceIndex index = indexes.FindOne(Query<AppInstanceIndex>.EQ(x => x.AppId, appId));

            if (index == null)
            {
                index = new AppInstanceIndex();
                index.AppId = "Tester";
                index.UseDefaultConnectionString = true;
                index.DatabaseName = "";
                index.Name = "OperationPlugins Tester";
                index.IsEnabled = true;
                indexes.Insert(index);
            }

            MongoCollection<AppInstance> instances = GetAppInstanceDatabase(index).GetCollection<AppInstance>("instances");
            AppInstance appInstance = instances.FindOne(Query<AppInstance>.EQ(x => x.Id, index.Id.ToString()));

            if (appInstance == null)
            {
                appInstance = new AppInstance();
                appInstance.Id = index.Id.ToString();
                appInstance.Name = "OperationPlugins Tester";
                appInstance.Accounts.Add(new Account { IsAnonymous = true });
                appInstance.Accounts.Add(new Account { Username = "admin", Password = "admin" });
                appInstance.Services.Add(new Service { IsEnabled = true, Id = "Validator" });
                appInstance.Services.Add(new Service { IsEnabled = true, Id = "Handler" });
                appInstance.PayloadContextTypeMappings.Add(new PayloadContextTypeMapping { IsEnabled = true, PayloadContentAlias = "Person", TypeName = "Tester.Shared.Person" });
                instances.Insert(appInstance);
            }


            return appInstance;
        }

        /// <summary>
        /// Saves the data of application instance.
        /// </summary>
        /// <param name="appInstance">The data of application instance to save.</param>
        void IApplicationDataContext.SaveAppInstance(AppInstance appInstance)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Removes the data of application instance.
        /// </summary>
        /// <param name="id">id</param>
        void IApplicationDataContext.RemoveAppInstance(string id)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the binary data.
        /// </summary>
        /// <param name="binaryDataId">binary data id</param>
        /// <returns>The requested binary data or null if not found.</returns>
        BinaryData IApplicationDataContext.GetBinaryDataByBinaryDataId(string binaryDataId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Saves the binary data.
        /// </summary>
        /// <param name="binaryData">The binary data to save.</param>
        void IApplicationDataContext.SaveBinaryData(BinaryData binaryData)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Removes the binary data.
        /// </summary>
        /// <param name="binaryDataId">binary data id</param>
        void IApplicationDataContext.RemoveBinaryData(string binaryDataId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Initialize.
        /// </summary>
        [Export(typeof(IInitialize))]
        private sealed class Initializer : IInitialize
        {
            void IInitialize.Initialize(InitializationContext context)
            {
                //   Debugger.Break();
            }
        }


    }
}
